#include <stdio.h>
#include "sample_comm_nnie.h"
#include "sample_comm_svp.h"
#include "mpi_sys.h"

/*****************************************************************************
*   Prototype    : SAMPLE_COMM_SVP_NNIE_ParamDeinit
*   Description  : Deinit NNIE parameters
*   Input        : SAMPLE_SVP_NNIE_PARAM_S        *pstNnieParam     NNIE Parameter
*                  SAMPLE_SVP_NNIE_SOFTWARE_MEM_S *pstSoftWareMem   software mem
*
*
*
*
*   Output       :
*   Return Value :  HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
HI_S32 SAMPLE_COMM_SVP_NNIE_ParamDeinit(SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam)
{
	SAMPLE_SVP_CHECK_EXPR_RET(NULL == pstNnieParam,HI_INVALID_VALUE,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error, pstNnieParam can't be NULL!\n");

	if(0!=pstNnieParam->stTaskBuf.u64PhyAddr && 0!=pstNnieParam->stTaskBuf.u64VirAddr)
	{
		SAMPLE_SVP_MMZ_FREE(pstNnieParam->stTaskBuf.u64PhyAddr,pstNnieParam->stTaskBuf.u64VirAddr);
		pstNnieParam->stTaskBuf.u64PhyAddr = 0;
		pstNnieParam->stTaskBuf.u64VirAddr = 0;
	}

    if(0!=pstNnieParam->stStepBuf.u64PhyAddr && 0!=pstNnieParam->stStepBuf.u64VirAddr)
	{
		SAMPLE_SVP_MMZ_FREE(pstNnieParam->stStepBuf.u64PhyAddr,pstNnieParam->stStepBuf.u64VirAddr);
		pstNnieParam->stStepBuf.u64PhyAddr = 0;
		pstNnieParam->stStepBuf.u64VirAddr = 0;
	}
	return HI_SUCCESS;
}

/*****************************************************************************
*   Prototype    : SAMPLE_SVP_NNIE_FillForwardInfo
*   Description  : fill NNIE forward ctrl information
*   Input        : SAMPLE_SVP_NNIE_CFG_S   *pstNnieCfg       NNIE configure info
* 	               SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam     NNIE parameter
*
*
*
*   Output       :
*   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
static HI_S32 SAMPLE_SVP_NNIE_FillForwardInfo(SAMPLE_SVP_NNIE_CFG_S *pstNnieCfg,
    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam)
{
	HI_U32 i = 0, j = 0; // i为某个段  j为段中的某个源节点 或 目标节点
	HI_U32 u32Offset = 0; // 没啥用？ 一直为0？
	HI_U32 u32Num = 0; // 1 batch最多输入图片数量

	for(i = 0; i < pstNnieParam->pstModel->u32NetSegNum; i++) // 遍历所有段
	{
        /*fill forwardCtrl info*/
		if(SVP_NNIE_NET_TYPE_ROI == pstNnieParam->pstModel->astSeg[i].enNetType) // 段的类型为roi
		{
			pstNnieParam->astForwardWithBboxCtrl[i].enNnieId = pstNnieCfg->aenNnieCoreId[i];
			pstNnieParam->astForwardWithBboxCtrl[i].u32SrcNum = pstNnieParam->pstModel->astSeg[i].u16SrcNum;
			pstNnieParam->astForwardWithBboxCtrl[i].u32DstNum = pstNnieParam->pstModel->astSeg[i].u16DstNum;
			pstNnieParam->astForwardWithBboxCtrl[i].u32ProposalNum = 1;
			pstNnieParam->astForwardWithBboxCtrl[i].u32NetSegId = i;
			pstNnieParam->astForwardWithBboxCtrl[i].stTmpBuf = pstNnieParam->stTmpBuf;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u64PhyAddr= pstNnieParam->stTaskBuf.u64PhyAddr+u32Offset;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u64VirAddr= pstNnieParam->stTaskBuf.u64VirAddr+u32Offset;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u32Size= pstNnieParam->au32TaskBufSize[i];
		}
		else if(SVP_NNIE_NET_TYPE_CNN == pstNnieParam->pstModel->astSeg[i].enNetType ||
            SVP_NNIE_NET_TYPE_RECURRENT== pstNnieParam->pstModel->astSeg[i].enNetType) // 段的类型为CNN 或 循环
		{
			pstNnieParam->astForwardCtrl[i].enNnieId = pstNnieCfg->aenNnieCoreId[i]; // 根据配置信息，使用哪个nnie
			pstNnieParam->astForwardCtrl[i].u32SrcNum = pstNnieParam->pstModel->astSeg[i].u16SrcNum; // 源节点数
			pstNnieParam->astForwardCtrl[i].u32DstNum = pstNnieParam->pstModel->astSeg[i].u16DstNum; // 目标节点数
			pstNnieParam->astForwardCtrl[i].u32NetSegId = i; // 段id
			pstNnieParam->astForwardCtrl[i].stTmpBuf = pstNnieParam->stTmpBuf; // ?? 每个段的临时缓存都指向一个内存地方？   为0(没用上？)
			pstNnieParam->astForwardCtrl[i].stTskBuf.u64PhyAddr= pstNnieParam->stTaskBuf.u64PhyAddr+u32Offset; // tsk在内存上地址  为0(没用上？)
			pstNnieParam->astForwardCtrl[i].stTskBuf.u64VirAddr= pstNnieParam->stTaskBuf.u64VirAddr+u32Offset;
			pstNnieParam->astForwardCtrl[i].stTskBuf.u32Size= pstNnieParam->au32TaskBufSize[i]; // tsk存储大小   为0(没用上？)
			// 经过printf，确实都是0，这些辅助内存应该是给其他类型的网络结构用的把(如循环?)
			// printf("%dth astSeg\n",i);
			// printf("TmpBuf: %lld  %lld\n",	pstNnieParam->stTmpBuf.u64PhyAddr, 
			// 							pstNnieParam->stTmpBuf.u64VirAddr);
			// printf("TskBuf: %lld  %lld  %d\n",	pstNnieParam->stTaskBuf.u64PhyAddr+u32Offset, 
			// 								pstNnieParam->stTaskBuf.u64VirAddr+u32Offset, 
			// 								pstNnieParam->au32TaskBufSize[i]); 
		}
		u32Offset += pstNnieParam->au32TaskBufSize[i]; // 并未创建缓存去  一直为0(没用上？)

        /*fill src blob info*/
		for(j = 0; j < pstNnieParam->pstModel->astSeg[i].u16SrcNum; j++) // 在每段中，继续遍历该段源节点
	    {
            /*Recurrent blob*/
            if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->pstModel->astSeg[i].astSrcNode[j].enType) // 多段有符32bit排布
            {
                pstNnieParam->astSegData[i].astSrc[j].enType = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].enType; // 节点数据类型排布形式(U8 bgr)
                pstNnieParam->astSegData[i].astSrc[j].unShape.stSeq.u32Dim = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].unShape.u32Dim;
                pstNnieParam->astSegData[i].astSrc[j].u32Num = pstNnieCfg->u32MaxInputNum;
                pstNnieParam->astSegData[i].astSrc[j].unShape.stSeq.u64VirAddrStep = pstNnieCfg->au64StepVirAddr[i*SAMPLE_SVP_NNIE_EACH_SEG_STEP_ADDR_NUM];
            }
            else // 一般为 SVP_BLOB_TYPE_U8 数据排布(bgr)
            {
    		    pstNnieParam->astSegData[i].astSrc[j].enType = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].enType; // 该段某个节点的类型
    	        pstNnieParam->astSegData[i].astSrc[j].unShape.stWhc.u32Chn = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].unShape.stWhc.u32Chn; // 节点内存的向量维度
    	        pstNnieParam->astSegData[i].astSrc[j].unShape.stWhc.u32Height = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].unShape.stWhc.u32Height; // 高
    	        pstNnieParam->astSegData[i].astSrc[j].unShape.stWhc.u32Width = pstNnieParam->pstModel->astSeg[i].astSrcNode[j].unShape.stWhc.u32Width; // 宽
    	        pstNnieParam->astSegData[i].astSrc[j].u32Num = pstNnieCfg->u32MaxInputNum; // 1 batch最多输入图片数量
            }
	    }

        /*fill dst blob info*/
		if(SVP_NNIE_NET_TYPE_ROI == pstNnieParam->pstModel->astSeg[i].enNetType) // 段的类型为roi
		{
			u32Num = pstNnieCfg->u32MaxRoiNum*pstNnieCfg->u32MaxInputNum;
		}
		else // 段的类型为 CNN 或 循环
		{
			u32Num = pstNnieCfg->u32MaxInputNum;// 1 batch最多输入图片数量
		}

		for(j = 0; j < pstNnieParam->pstModel->astSeg[i].u16DstNum; j++) // 遍历每个目标节点
		{
            if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->pstModel->astSeg[i].astDstNode[j].enType) // 多段有符32bit排布
            {
    			pstNnieParam->astSegData[i].astDst[j].enType = pstNnieParam->pstModel->astSeg[i].astDstNode[j].enType;
    			pstNnieParam->astSegData[i].astDst[j].unShape.stSeq.u32Dim =
                    pstNnieParam->pstModel->astSeg[i].astDstNode[j].unShape.u32Dim;
                pstNnieParam->astSegData[i].astDst[j].u32Num = u32Num;
                pstNnieParam->astSegData[i].astDst[j].unShape.stSeq.u64VirAddrStep =
                    pstNnieCfg->au64StepVirAddr[i*SAMPLE_SVP_NNIE_EACH_SEG_STEP_ADDR_NUM+1];
            }
            else // 一般为 SVP_BLOB_TYPE_U8 数据排布(bgr)
            {
    		    pstNnieParam->astSegData[i].astDst[j].enType = pstNnieParam->pstModel->astSeg[i].astDstNode[j].enType; // 该段某个节点的类型
    		    pstNnieParam->astSegData[i].astDst[j].unShape.stWhc.u32Chn = pstNnieParam->pstModel->astSeg[i].astDstNode[j].unShape.stWhc.u32Chn;
    		    pstNnieParam->astSegData[i].astDst[j].unShape.stWhc.u32Height = pstNnieParam->pstModel->astSeg[i].astDstNode[j].unShape.stWhc.u32Height;
    		    pstNnieParam->astSegData[i].astDst[j].unShape.stWhc.u32Width = pstNnieParam->pstModel->astSeg[i].astDstNode[j].unShape.stWhc.u32Width;
    		    pstNnieParam->astSegData[i].astDst[j].u32Num = u32Num; // 1 batch最多输入图片数量
            }
		}
	}
	return HI_SUCCESS;
}

/*****************************************************************************
*   Prototype    : SAMPLE_SVP_NNIE_GetBlobMemSize
*   Description  : Get blob mem size
*   Input        : SVP_NNIE_NODE_S astNnieNode[]   NNIE Node  	节点信息的首指针
*                  HI_U32          u32NodeNum      Node num		节点数目	通过前两者，就可以得到所有节点信息了
									u32TotalStep				循环网络结构用到，一般为0
*                  HI_U32          astBlob[]       blob struct	blob结构首指针？ 	输出，其中u32Stride分量标示每个blob一行的byte数
*                  HI_U32          u32Align        stride align type	跨度类型 16个一对齐？
*                  HI_U32          *pu32TotalSize  Total size	输出，所有节点blob的byte数
*                  HI_U32          au32BlobSize[]  blob size	输出，每个blob所占byte数
*
*
*
*
*   Output       :
*   Return Value : VOID
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
static void SAMPLE_SVP_NNIE_GetBlobMemSize(SVP_NNIE_NODE_S astNnieNode[], HI_U32 u32NodeNum,
	HI_U32 u32TotalStep,SVP_BLOB_S astBlob[], HI_U32 u32Align, HI_U32* pu32TotalSize,HI_U32 au32BlobSize[])
{
	HI_U32 i = 0;
	HI_U32 u32Size = 0;
	HI_U32 u32Stride = 0;

	for(i = 0; i < u32NodeNum; i++) // 对每一个节点操作
	{
		if(SVP_BLOB_TYPE_S32== astNnieNode[i].enType||SVP_BLOB_TYPE_VEC_S32== astNnieNode[i].enType||
            SVP_BLOB_TYPE_SEQ_S32== astNnieNode[i].enType) // 各类储存blob类型，总之我们不是这些
		{
			u32Size = sizeof(HI_U32);
		}
		else // u8 
		{
			u32Size = sizeof(HI_U8); // 一个像素一个通道占1byte
		}

        if(SVP_BLOB_TYPE_SEQ_S32 == astNnieNode[i].enType)
        {
            if(SAMPLE_SVP_NNIE_ALIGN_16 == u32Align) // nnie的stride是以byte为单位
    		{
    			u32Stride = SAMPLE_SVP_NNIE_ALIGN16(astNnieNode[i].unShape.u32Dim*u32Size);
    		}
    		else
    		{
    			u32Stride = SAMPLE_SVP_NNIE_ALIGN32(astNnieNode[i].unShape.u32Dim*u32Size);
    		}
            au32BlobSize[i] = u32TotalStep*u32Stride;
        }
        else // 节点类型 这里
        {
            if(SAMPLE_SVP_NNIE_ALIGN_16 == u32Align) // 我们就是16  16个一对齐？
    		{
				// 该步骤求取＞width的最小的16的倍数；
				// blob放在一块内存中，width根据具体输入来定，为了加快硬件读取速度，实现跨行读取，最好让每一行都是256byte的奇数倍，但是求不得
				// 退而求其次，按16ybte的方式对其，一行不够16byte的地方，为无效字节
    			u32Stride = SAMPLE_SVP_NNIE_ALIGN16(astNnieNode[i].unShape.stWhc.u32Width*u32Size); 
				// printf("blob width: %d   stride: %d\n", astNnieNode[i].unShape.stWhc.u32Width*u32Size, u32Stride);
				// 打印结果，例如300-304、504-512、336-336等,单位byte
    		}
    		else
    		{
    			u32Stride = SAMPLE_SVP_NNIE_ALIGN32(astNnieNode[i].unShape.stWhc.u32Width*u32Size);
    		}

			// printf("astBlob[%d].u32Num = %d\n", i, astBlob[i].u32Num); // 全部为1，batch数？
			// 一个节点的blob所占总byte数
    		au32BlobSize[i] = astBlob[i].u32Num* // 打印结果全为1,应该是batch数
				u32Stride* // 填补后，blob一行的byte数
				astNnieNode[i].unShape.stWhc.u32Height*
    			astNnieNode[i].unShape.stWhc.u32Chn;
        }
		*pu32TotalSize += au32BlobSize[i]; //所有节点 blob所占byte数
	    astBlob[i].u32Stride = u32Stride; // 每个blob一行的byte数
	}
}


/*****************************************************************************
*   Prototype    : SAMPLE_SVP_NNIE_GetTaskAndBlobBufSize
*   Description  : Get taskinfo and blob memory size
*   Input        : SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam     NNIE parameter
* 	                HI_U32                  *pu32TaskInfoSize Task info size
*                  HI_U32                  *pu32TmpBufSize    Tmp buffer size
*                  SAMPLE_SVP_NNIE_BLOB_SIZE_S  astBlobSize[] each seg input and output blob mem size
*                  HI_U32                  *pu32TotalSize     Total mem size
*
*
*   Output       :
*   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
static HI_S32 SAMPLE_SVP_NNIE_GetTaskAndBlobBufSize(SAMPLE_SVP_NNIE_CFG_S *pstNnieCfg,
    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,HI_U32*pu32TotalTaskBufSize, HI_U32*pu32TmpBufSize,
    SAMPLE_SVP_NNIE_BLOB_SIZE_S astBlobSize[],HI_U32*pu32TotalSize)
{
	HI_S32 s32Ret = HI_SUCCESS;
	HI_U32 i = 0, j = 0;
    HI_U32 u32TotalStep = 0; // 不需要用到，循环类型的段采用考虑的内存存储

	/*Get each seg's task buf size*/
	// 解析的网络模型、batch数、roi数、段落数 得到每段nnie需要的辅助内存大小,保存于 pstNnieParam->au32TaskBufSize
	s32Ret = HI_MPI_SVP_NNIE_GetTskBufSize(pstNnieCfg->u32MaxInputNum, pstNnieCfg->u32MaxRoiNum,
		pstNnieParam->pstModel, pstNnieParam->au32TaskBufSize,pstNnieParam->pstModel->u32NetSegNum);
	SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,HI_MPI_SVP_NNIE_GetTaskSize failed!\n");

    /*Get total task buf size*/
	// 个人理解：任务需要的缓存大小，根据段的个数决定，结合API手册中HI_MPI_SVP_NNIE_GetTskBufSize函数注意事项
	// 函数注意事项为：单线程，nnie → nnie这种连续N个nnie执行时，只有1个nnie可以复用前面的nnie的tskbuf，
	// 而在nnie → 非nnie这种没有连续nnie时，是可以不断复用tskbuf的
	*pu32TotalTaskBufSize = 0; // 并不是！→ 推测是储存nnie段结果的(是不是因为硬件与硬件之间传输数据，必须要提前指定好数据流位置，不能复用)
	for(i = 0; i < pstNnieParam->pstModel->u32NetSegNum; i++)
	{
		*pu32TotalTaskBufSize += pstNnieParam->au32TaskBufSize[i]; // 把每段nnie需要的缓存数求和
		// printf("%dth tskbuf size: %d\n",i+1,pstNnieParam->au32TaskBufSize[i]); // 我倒要看看这tskbuf究竟是何物！！
	}

	/*Get tmp buf size (temporary)*/
	*pu32TmpBufSize = pstNnieParam->pstModel->u32TmpBufSize; // 个人理解：临时缓存大小，由于是模型解析得到的，推测为储存模型权重
	// printf("tmpbuf size: %d\n",pstNnieParam->pstModel->u32TmpBufSize); // tmpbuf你也别想逃！！
	*pu32TotalSize += *pu32TotalTaskBufSize + *pu32TmpBufSize; // 所有buf大小

	/*calculate Blob mem size*/
	for(i = 0; i < pstNnieParam->pstModel->u32NetSegNum; i++) // 对每一段
	{
        if(SVP_NNIE_NET_TYPE_RECURRENT == pstNnieParam->pstModel->astSeg[i].enNetType) // 段类型为循环
        {
            for(j = 0; j < pstNnieParam->astSegData[i].astSrc[0].u32Num; j++)
            {
                u32TotalStep += *((HI_S32*)pstNnieParam->astSegData[i].astSrc[0].unShape.stSeq.u64VirAddrStep+j);
            }
        }
		/*the first seg's Src Blob mem size, other seg's src blobs from the output blobs of
		those segs before it or from software output results*/
		// nnie只需考虑第一段的来源，其他段blob来源于前面的段或软件结果
		if(i == 0) // 第一段 来源
		{
			// 得到图像输入的blob的内存分配方式：一行byte数多少(一行的byte需要按16byte的倍数对齐，可以加快读取速度)，每个blob多少byte，总共多少byte
			SAMPLE_SVP_NNIE_GetBlobMemSize(&(pstNnieParam->pstModel->astSeg[i].astSrcNode[0]), // 储存节点信息的首指针
				pstNnieParam->pstModel->astSeg[i].u16SrcNum, // 源节点数目
				u32TotalStep, // (循环网络结构用到，为0)
				&(pstNnieParam->astSegData[i].astSrc[0]), // 所有blob结构指针，里面有个分量u32Stride代表每个blob一行的byte数
				SAMPLE_SVP_NNIE_ALIGN_16, // 16byte的对其方式
				pu32TotalSize, // 得到所有节点blob所占byte数
				&(astBlobSize[i].au32SrcSize[0])); // 得到每个blob大小，这里只有第0段第0个blob大小
		}

		// 所有段nnie目标blob
		// 得到每个段输出的blob的内存分配方式：一行byte数多少(一行的byte需要按16byte的倍数对齐，可以加快读取速度)，每个blob多少byte，总共多少byte
		/*Get each seg's Dst Blob mem size*/
		SAMPLE_SVP_NNIE_GetBlobMemSize(&(pstNnieParam->pstModel->astSeg[i].astDstNode[0]),
			pstNnieParam->pstModel->astSeg[i].u16DstNum,
			u32TotalStep,
			&(pstNnieParam->astSegData[i].astDst[0]),
			SAMPLE_SVP_NNIE_ALIGN_16,
			pu32TotalSize, 
			&(astBlobSize[i].au32DstSize[0]));
	}
	return s32Ret;
}

/*****************************************************************************
*   Prototype    : SAMPLE_SVP_NNIE_ParamInit
*   Description  : Fill info of NNIE Forward parameters
*   Input        : SAMPLE_SVP_NNIE_CFG_S   *pstNnieCfg    NNIE configure parameter
* 		            SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam	 NNIE parameters
*
*
*
*   Output       :
*   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-03-14
*           Author       :
*           Modification : Create
*
*****************************************************************************/
static HI_S32 SAMPLE_SVP_NNIE_ParamInit(SAMPLE_SVP_NNIE_CFG_S *pstNnieCfg,
    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam)
{
	HI_U32 i = 0, j = 0;
	HI_U32 u32TotalSize = 0; // tskbuf+tmpbuf+blob的总大小
	HI_U32 u32TotalTaskBufSize = 0; // Tskbuf大小，存储nnie里的一些辅助信息，空间不大(Faster 第二段很大，其他都200byte以下)？
	HI_U32 u32TmpBufSize = 0; // Tmpbuf大小，存储模型权重？
	HI_S32 s32Ret = HI_SUCCESS;
	HI_U32 u32Offset = 0;
	HI_U64 u64PhyAddr = 0;
	HI_U8 *pu8VirAddr = NULL;
	SAMPLE_SVP_NNIE_BLOB_SIZE_S astBlobSize[SVP_NNIE_MAX_NET_SEG_NUM] = {0}; // 每个段中源/目标blob的大小(仅计算了第一个源和所有的目标)

	/*fill forward info*/
	// 根据解析的模型，填充每个段的信息：使用哪个nnie、段id、源目标节点数，以及每个节点的blob类型(U8)、通道、高、宽、batch数(为1)
	s32Ret = SAMPLE_SVP_NNIE_FillForwardInfo(pstNnieCfg,pstNnieParam); 
	SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,SAMPLE_SVP_NNIE_FillForwardCtrl failed!\n");

	/*Get taskInfo and Blob mem size*/
	// 输出：总TaskBuf大小、TmpBuf大小、各节点blob大小、总大小
	s32Ret = SAMPLE_SVP_NNIE_GetTaskAndBlobBufSize(pstNnieCfg,pstNnieParam,&u32TotalTaskBufSize,
		&u32TmpBufSize,astBlobSize,&u32TotalSize);
	SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,SAMPLE_SVP_NNIE_GetTaskAndBlobBufSize failed!\n");

	/*Malloc mem*/
	s32Ret = SAMPLE_COMM_SVP_MallocCached("SAMPLE_NNIE_TASK",NULL,(HI_U64*)&u64PhyAddr,(void**)&pu8VirAddr,u32TotalSize); // 分配所有空间 内存，暂存于cache中
	SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,Malloc memory failed!\n");
	memset(pu8VirAddr, 0, u32TotalSize); // 置0，仅需要虚拟地址就可以memset？
	SAMPLE_COMM_SVP_FlushCache(u64PhyAddr,(void*)pu8VirAddr,u32TotalSize); // API 刷新cache到内存，并清楚cache

	/*fill taskinfo mem addr*/
	// tskbuf先分配一整块，然后根据段拆分成不同小份
	pstNnieParam->stTaskBuf.u32Size = u32TotalTaskBufSize;
	pstNnieParam->stTaskBuf.u64PhyAddr = u64PhyAddr;
	pstNnieParam->stTaskBuf.u64VirAddr = (HI_U64)pu8VirAddr;

	/*fill Tmp mem addr*/
	// tmp是一整块,每段都用的是同一个地址
	pstNnieParam->stTmpBuf.u32Size = u32TmpBufSize;
	pstNnieParam->stTmpBuf.u64PhyAddr = u64PhyAddr+u32TotalTaskBufSize; // 惊了，物理地址和虚拟地址到底什么关系？怎么都需要偏移相同大小
	pstNnieParam->stTmpBuf.u64VirAddr = (HI_U64)pu8VirAddr+u32TotalTaskBufSize;

	/*fill forward ctrl addr*/
	// 将tmpbuf和tskbuf地址放到前向传播控制结构体中
	for(i = 0; i < pstNnieParam->pstModel->u32NetSegNum; i++) // 遍历每个段
	{
		if(SVP_NNIE_NET_TYPE_ROI == pstNnieParam->pstModel->astSeg[i].enNetType)
		{
			pstNnieParam->astForwardWithBboxCtrl[i].stTmpBuf = pstNnieParam->stTmpBuf;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u64PhyAddr= pstNnieParam->stTaskBuf.u64PhyAddr+u32Offset;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u64VirAddr= pstNnieParam->stTaskBuf.u64VirAddr+u32Offset;
			pstNnieParam->astForwardWithBboxCtrl[i].stTskBuf.u32Size= pstNnieParam->au32TaskBufSize[i];
		}
		else if(SVP_NNIE_NET_TYPE_CNN == pstNnieParam->pstModel->astSeg[i].enNetType || // 段是这个类型
            SVP_NNIE_NET_TYPE_RECURRENT == pstNnieParam->pstModel->astSeg[i].enNetType)
		{
			pstNnieParam->astForwardCtrl[i].stTmpBuf = pstNnieParam->stTmpBuf; // 所有段共用同一个tmpbuf
			pstNnieParam->astForwardCtrl[i].stTskBuf.u64PhyAddr= pstNnieParam->stTaskBuf.u64PhyAddr+u32Offset; // 不同段需要用不同的tskbuf
			pstNnieParam->astForwardCtrl[i].stTskBuf.u64VirAddr= pstNnieParam->stTaskBuf.u64VirAddr+u32Offset;
			pstNnieParam->astForwardCtrl[i].stTskBuf.u32Size= pstNnieParam->au32TaskBufSize[i];
		}
		u32Offset += pstNnieParam->au32TaskBufSize[i]; // 下个段的tskbuf偏移计算
	}

	/*fill each blob's mem addr*/
	u64PhyAddr =  u64PhyAddr+u32TotalTaskBufSize+u32TmpBufSize;
	pu8VirAddr = pu8VirAddr+u32TotalTaskBufSize+u32TmpBufSize;
	for(i = 0; i < pstNnieParam->pstModel->u32NetSegNum; i++) // i为段
	{
		/*first seg has src blobs, other seg's src blobs from the output blobs of
		those segs before it or from software output results*/
		if(0 == i) // 第一段的源节点
		{
			for(j = 0; j < pstNnieParam->pstModel->astSeg[i].u16SrcNum; j++) // j为节点
			{
				if(j!=0) // 用于偏移 如果有多个源节点，需要在第二个节点后，每次加上一个偏移
				{
					u64PhyAddr += astBlobSize[i].au32SrcSize[j-1]; 
					pu8VirAddr += astBlobSize[i].au32SrcSize[j-1];
				}
				// 给图片源节点地址
				pstNnieParam->astSegData[i].astSrc[j].u64PhyAddr = u64PhyAddr;
				pstNnieParam->astSegData[i].astSrc[j].u64VirAddr = (HI_U64)pu8VirAddr;
			}
			u64PhyAddr += astBlobSize[i].au32SrcSize[j-1]; // 继续偏移
			pu8VirAddr += astBlobSize[i].au32SrcSize[j-1];
		}

		/*fill the mem addrs of each seg's output blobs*/
		// 目标节点blob分配空间
		for(j = 0; j < pstNnieParam->pstModel->astSeg[i].u16DstNum; j++)
		{
			if(j!=0) // 用于偏移 如果有多个目标节点，需要在第二个节点后，每次加上一个偏移
			{
				u64PhyAddr += astBlobSize[i].au32DstSize[j-1];
				pu8VirAddr += astBlobSize[i].au32DstSize[j-1];
			}
			// 给图片目标节点地址
			pstNnieParam->astSegData[i].astDst[j].u64PhyAddr = u64PhyAddr;
			pstNnieParam->astSegData[i].astDst[j].u64VirAddr = (HI_U64)pu8VirAddr;
		}
		u64PhyAddr += astBlobSize[i].au32DstSize[j-1]; // 继续偏移
		pu8VirAddr += astBlobSize[i].au32DstSize[j-1];
	}
	return s32Ret;
}

/*****************************************************************************
*   Prototype    : SAMPLE_COMM_SVP_NNIE_ParamInit
*   Description  : Init NNIE  parameters
*   Input        : SAMPLE_SVP_NNIE_CFG_S   *pstNnieCfg    NNIE configure parameter
*                  SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam    NNIE parameters
*
*
*
*   Output       :
*   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
HI_S32 SAMPLE_COMM_SVP_NNIE_ParamInit(SAMPLE_SVP_NNIE_CFG_S *pstNnieCfg,
    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam)
{
	HI_S32 s32Ret = HI_SUCCESS;

    /*check*/
    SAMPLE_SVP_CHECK_EXPR_RET((NULL == pstNnieCfg || NULL == pstNnieParam),HI_ERR_SVP_NNIE_ILLEGAL_PARAM,
        SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,pstNnieCfg and pstNnieParam can't be NULL!\n");
    SAMPLE_SVP_CHECK_EXPR_RET((NULL == pstNnieParam->pstModel),HI_ERR_SVP_NNIE_ILLEGAL_PARAM,
        SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,pstNnieParam->pstModel can't be NULL!\n");

	/*NNIE parameter initialization */
	s32Ret = SAMPLE_SVP_NNIE_ParamInit(pstNnieCfg,pstNnieParam);
	SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,FAIL,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error, SAMPLE_SVP_NNIE_ParamInit failed!\n");

	return s32Ret;
FAIL:
	s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(pstNnieParam);
	SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error, SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
	return HI_FAILURE;
}

/*****************************************************************************
 *   Prototype    : SAMPLE_COMM_SVP_NNIE_UnloadModel
 *   Description  : unload NNIE model
 *   Input        : SAMPLE_SVP_NNIE_MODEL_S *pstNnieModel      NNIE Model
 *
 *
 *
 *   Output       :
 *   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
 *   Spec         :
 *   Calls        :
 *   Called By    :
 *   History:
 *
 *       1.  Date         : 2017-11-20
 *           Author       :
 *           Modification : Create
 *
 *****************************************************************************/
HI_S32 SAMPLE_COMM_SVP_NNIE_UnloadModel(SAMPLE_SVP_NNIE_MODEL_S *pstNnieModel)
{
	if(0!=pstNnieModel->stModelBuf.u64PhyAddr && 0!=pstNnieModel->stModelBuf.u64VirAddr)
	{
		SAMPLE_SVP_MMZ_FREE(pstNnieModel->stModelBuf.u64PhyAddr,pstNnieModel->stModelBuf.u64VirAddr);
		pstNnieModel->stModelBuf.u64PhyAddr = 0;
		pstNnieModel->stModelBuf.u64VirAddr = 0;
	}
    return HI_SUCCESS;
}


/*****************************************************************************
 *   Prototype    : SAMPLE_COMM_SVP_NNIE_LoadModel
 *   Description  : load NNIE model
 *   Input        : HI_CHAR                 * pszModelFile    Model file name
 *                  SAMPLE_SVP_NNIE_MODEL_S *pstNnieModel     NNIE Model
 *
 *
 *
 *   Output       :
 *   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
 *   Spec         :
 *   Calls        :
 *   Called By    :
 *   History:
 *
 *       1.  Date         : 2017-11-20
 *           Author       :
 *           Modification : Create
 *
 *****************************************************************************/
HI_S32 SAMPLE_COMM_SVP_NNIE_LoadModel(HI_CHAR * pszModelFile,
    SAMPLE_SVP_NNIE_MODEL_S *pstNnieModel)
{
	HI_S32 s32Ret = HI_INVALID_VALUE;
	HI_U64 u64PhyAddr = 0;
	HI_U8 *pu8VirAddr = NULL;
	HI_SL slFileSize = 0; // 存储.wk文件大小,  单位：字节(8bit)
	/*Get model file size*/
	FILE *fp=fopen(pszModelFile,"rb"); // 打开.wk文件，读取二进制文件
	SAMPLE_SVP_CHECK_EXPR_RET(NULL == fp,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error, open model file failed!\n");
    s32Ret = fseek(fp,0L,SEEK_END); // 将位流指针 放于 文件尾部
	SAMPLE_SVP_CHECK_EXPR_GOTO(-1 == s32Ret,FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error, fseek failed!\n");
    slFileSize = ftell(fp); // 得到当前指针相对文件首的偏移，即文件大小  单位：字节(8bit)
	SAMPLE_SVP_CHECK_EXPR_GOTO(slFileSize <= 0,FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error, ftell failed!\n");
	s32Ret = fseek(fp,0L,SEEK_SET); // 将位流指针 放于 文件开头
	SAMPLE_SVP_CHECK_EXPR_GOTO(-1 == s32Ret,FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error, fseek failed!\n");

	/*malloc model file mem*/
	s32Ret = SAMPLE_COMM_SVP_MallocMem("SAMPLE_NNIE_MODEL",NULL,(HI_U64*)&u64PhyAddr,(void**)&pu8VirAddr,slFileSize);
	SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error(%#x),Malloc memory failed!\n",s32Ret);

	pstNnieModel->stModelBuf.u32Size = (HI_U32)slFileSize;
	pstNnieModel->stModelBuf.u64PhyAddr = u64PhyAddr;
	pstNnieModel->stModelBuf.u64VirAddr = (HI_U64)pu8VirAddr;

    s32Ret = fread(pu8VirAddr,slFileSize,1,fp); // 读取.wk
	SAMPLE_SVP_CHECK_EXPR_GOTO(1 != s32Ret,FAIL_1,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,read model file failed!\n");

	/*load model*/
	s32Ret = HI_MPI_SVP_NNIE_LoadModel(&pstNnieModel->stModelBuf,&pstNnieModel->stModel);
	SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,FAIL_1,SAMPLE_SVP_ERR_LEVEL_ERROR,
		"Error,HI_MPI_SVP_NNIE_LoadModel failed!\n");

	fclose(fp);
    return HI_SUCCESS;
FAIL_1:
    SAMPLE_SVP_MMZ_FREE(pstNnieModel->stModelBuf.u64PhyAddr,pstNnieModel->stModelBuf.u64VirAddr);
    pstNnieModel->stModelBuf.u32Size  = 0;
FAIL_0:
    if (NULL != fp)
    {
        fclose(fp);
    }

	return HI_FAILURE;
}


/*****************************************************************************
*   Prototype    : SAMPLE_COMM_SVP_NNIE_FillRect
*   Description  : Draw rect
*   Input        : VIDEO_FRAME_INFO_S             *pstFrmInfo   Frame info
* 		            SAMPLE_SVP_NNIE_RECT_ARRAY_S  *pstRect       Rect
*                  HI_U32                         u32Color      Color
*
*
*   Output       :
*   Return Value : HI_S32,HI_SUCCESS:Success,Other:failure
*   Spec         :
*   Calls        :
*   Called By    :
*   History:
*
*       1.  Date         : 2017-11-20
*           Author       :
*           Modification : Create
*
*****************************************************************************/
HI_S32 SAMPLE_COMM_SVP_NNIE_FillRect(VIDEO_FRAME_INFO_S *pstFrmInfo, SAMPLE_SVP_NNIE_RECT_ARRAY_S* pstRect, HI_U32 u32Color)
{
    VGS_HANDLE VgsHandle = -1;
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 i,j;
    VGS_TASK_ATTR_S stVgsTask;
    VGS_ADD_COVER_S stVgsAddCover;
    static HI_U32 u32Frm = 0;
    u32Frm++;
    if (0 == pstRect->u32TotalNum)
    {
        return s32Ret;
    }
    s32Ret = HI_MPI_VGS_BeginJob(&VgsHandle);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("Vgs begin job fail,Error(%#x)\n", s32Ret);
        return s32Ret;
    }

    memcpy(&stVgsTask.stImgIn, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));
    memcpy(&stVgsTask.stImgOut, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));

    stVgsAddCover.enCoverType = COVER_QUAD_RANGLE;
    stVgsAddCover.u32Color = u32Color;
    stVgsAddCover.stQuadRangle.bSolid = HI_FALSE;
    stVgsAddCover.stQuadRangle.u32Thick = 2;

    for (i = 0; i < pstRect->u32ClsNum; i++)
    {
        for (j = 0; j < pstRect->au32RoiNum[i]; j++)
        {
            memcpy(stVgsAddCover.stQuadRangle.stPoint, pstRect->astRect[i][j].astPoint, sizeof(pstRect->astRect[i][j].astPoint));
            s32Ret = HI_MPI_VGS_AddCoverTask(VgsHandle, &stVgsTask, &stVgsAddCover);
            if (s32Ret != HI_SUCCESS)
            {
                SAMPLE_PRT("HI_MPI_VGS_AddCoverTask fail,Error(%#x)\n", s32Ret);
                HI_MPI_VGS_CancelJob(VgsHandle);
                return s32Ret;
            }

        }

    }

    s32Ret = HI_MPI_VGS_EndJob(VgsHandle);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("HI_MPI_VGS_EndJob fail,Error(%#x)\n", s32Ret);
        HI_MPI_VGS_CancelJob(VgsHandle);
        return s32Ret;
    }

    return s32Ret;

}


